<?php
// Input validation. Supports type-description driven validation.
// Part of mmvc library, https://code.google.com/p/mmvc/
// Dependencies: None

/**
 * Input filtering class.
 * Filtering functions get pre-filtered input and optionally original un-filtered input as parameters.
 * Filtering functions return '' on empty string (unless otherwise stated), false if filtering failed and filtered value otherwise.
 */
class InputFilter {
	/** 
	 * Mapping of boolean values to 0/1 result. 
	 */
	public $boolValues = array(true=>1, '1'=>1, 'true'=>1, 'yes'=>1, 'on'=>1, 'enabled'=>1, '0'=>0, 'false'=>0, 'off'=>0, 'disabled'=>0, 'no'=>0, false=>0, ''=>0);
	/** 
	 * Flags used to pre-filter both keys and values before passing to user-defined filters. 
	 */
	public $filterFlags = FILTER_SANITIZE_STRING;
	/**
	 * IP filter accepts '*' wildcards. Default is true.
	 */
	public $ipWildcards = true;
	/**
	 * Maximum String length. Default is 255.
	 */
	public $maxStringLength = 255;
	/**
	 * Maximum Text length. Default is 65535.
	 */
	public $maxTextLength = 65535;
	
	/**
	 * Default input filter.
	 */
	function __construct() {
	}

	/**
	 * Basic string sanitizing filter which trims whitespace at the ends.
	 * If $this->filterFlags is set false, filtering is skipped.
	 * @return filtered string or false if filtering failed.
	 */
	function filterDefault( $v ) {
		return $this->filterFlags === false ? ''.$v : trim(filter_var(''.$v,$this->filterFlags));
	}
	
	/**
	 * Basic string filter with length limit of 255.
	 * @return filtered string or false if filtering failed.
	 */
	function filterString( $v ) {
		$v = $this->filterDefault($v);
		return strlen($v) <= $this->maxStringLength ? $v : false;
	}
	
	/**
	 * Basic string filter with length limit of 65535.
	 * @return filtered string or false if filtering failed.
	 */
	function filterText( $v ) {
		$v = $this->filterDefault($v);
		return strlen($v) <= $this->maxTextLength ? $v : false;
	}
		
	/** 
	 * Filters HH:MM(:SS) time value. 
	 * @return Time value string HH:MM:SS, '' on empty input and false if filtering failed.
	 */
	function filterTime( $v ) {
		if ( preg_match('/^(\d{2}):(\d{2}):(\d{2})$/',$v) )
			return $v;
		else if ( preg_match('/^(\d{2}):(\d{2})$/',$v) )
			return $v.':00';
		else if ($v === '')
			return $v;
		return false;
	}
	
	/**
	 * Filters boolean value to 0/1.
	 * @return 0 or 1 or false if filtering failed.
	 */
	function filterBool( $v ) {
		if ( is_string($v) ) 
			$v = strtolower($v);
		return isset($this->boolValues[$v]) ? $this->boolValues[$v] : false;
	}

	/**
	 * Removes all spaces from string.
	 * @param string $v
	 */
	function removeSpaces( $v ) {
		return is_string($v) ? preg_replace('/\s+/', '', '' . $v) : $v;
	}
	
	/**
	 * Filters real numeric value using ISO/C convention.
	 * @return Numeric value or false if filtering failed.
	 */
	function filterNumber( $v ) {
		$v = $this->removeSpaces($v);
		return $v==='' || preg_match( '/^[-+]?(\d+)(\.\d+)?$/', $v ) ? $v : false;
	}

	/**
	 * Filters integer value using ISO/C convention.
	 * @return Integer or false if filtering failed.
	 */
	function filterInt( $v ) {
		$v = $this->removeSpaces($v);
		return $v==='' || preg_match( '/^[-+]?(\d+)$/', $v ) ? $v : false;
	}

	/**
	 * ISO datetime filter. Y-m-d H:i:s, where time defaults to 00:00:00.
	 * @return datetime or false if not valid.
	 */
	function filterDateTime( $v ) {
		if ( preg_match('|^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?$|',$v) )
			return substr($v,0,4+3+3+1+2+3+3);
		if ( preg_match('|^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$|',$v) )
			return $v.':00';
		if ( preg_match('|^\d{4}-\d{2}-\d{2}$|',$v) )
			return $v.' 00:00:00';
		if ( preg_match('|^\d{4}-\d{2}$|',$v) )
			return $v.'-01 00:00:00';
		if ( preg_match('|^\d{4}$|',$v) )
			return $v.'-01-01 00:00:00';
		return !$v ? $v : false;
	}

	/**
	 * ISO date filter. Y-m-d (H:i:s), where time defaults to 00:00:00.
	 * @return date or false if not valid.
	 */
	function filterDate( $v ) {
		if ( preg_match('|^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?$|',$v) )
			return substr($v,0,4+3+3);
		if ( preg_match('|^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$|',$v) )
			return substr($v,0,4+3+3);
		if ( preg_match('|^\d{4}-\d{2}-\d{2}$|',$v) )
			return $v;
		if ( preg_match('|^\d{4}-\d{2}$|',$v) )
			return $v.'-01';
		if ( preg_match('|^\d{4}$|',$v) )
			return $v.'-01-01';
		return !$v ? $v : false;
	}

	/**
	 * Filters C-style variable name.
	 * @return Variable name or false if not valid.
	 */
	function filterVar( $v ) {
		return $v==='' || preg_match( '/^[\w_][\w_\d]*$/', $v ) ? $v : false;
	}

	/**
	 * Filters email address.
	 * @return Email address or false if not valid.
	 */
	function filterEmail( $v ) {
		return $v==='' || filter_var($v, FILTER_VALIDATE_EMAIL) ? $v : false;
	}

	/**
	 * Filters URL.
	 * @return URL or false if not valid.
	 */
	function filterURL( $v ) {
		return $v==='' || filter_var( $v, FILTER_VALIDATE_URL ) ? $v : false;
	}

	/**
	 * Filters IP address.
	 * @param $v IP to validate
	 * @return IP or false if not valid.
	 * @see InputFilter::ipWildcards
	 */
	function filterIP( $v ) {
		if ( $v === '' )
			return $v;
		return filter_var(($this->ipWildcards?str_replace('*','11',$v):$v), FILTER_VALIDATE_IP) ? $v : false;
	}
	
	/**
	 * Returns false if original string is empty.
	 */
	function filterRequired( $v, $v0 ) {
		return !$v0 || trim(''.$v0) ? $v : false;
	}
	
	/**
	 * Returns (UTF-8) input in upper-case.
	 */
	function filterUppercase( $v ) {
		return mb_strtoupper($v,'UTF-8');
	}

	/**
	 * Returns (UTF-8) input in lower-case.
	 */
	function filterLowercase( $v ) {
		return mb_strtolower($v,'UTF-8');
	}

	/**
	 * Returns zip-code filtered to minimum of 5-digit length.
	 */
	function filterZip( $v ) {
		return $v ? str_pad($v,5,'0',STR_PAD_LEFT) : $v;
	}
}

?>